#include "ggear_image.h"

namespace ggear
{
	namespace gfx
	{
		Image::Image()
			: mData(null), mPixelFmt(PixelFormat_R8G8B8A8)
		{ 
			/* Do nothing */
		}
	

		Image::Image(const Image& _img)
		{
			drefs(this) = _img;
		}


		Image::Image(u32 _width, u32 _height, PixelFormat _pxFmt)
			: mData(null), mPixelFmt(_pxFmt)
		{
			if(!_width || !_height) return;
			mWidth = _width;
			mHeight = _height;
			u32 totalSize = mWidth * mHeight * (ColorBits() / 8);
			mData = mAlloc.Allocate(totalSize);
			if(!mData) return;
			memset(mData, 0, totalSize);
		}


		Image::Image(const u8* const _data, u32 _width, u32 _height, PixelFormat _pxFmt)
			: mData(null), mPixelFmt(PixelFormat_R8G8B8A8)
		{
			if(isnull(_data)|| !_width || !_height) return;
		}


		Image::~Image()
		{
			Dispose();
		}


		u32 Image::Width() const
		{
			return mWidth;
		}


		u32 Image::Height() const
		{
			return mHeight;
		}


		Image::PixelFormat Image::ColorFormat() const
		{
			return mPixelFmt;
		}


		u8 Image::ColorBits() const
		{
			switch(mPixelFmt)
			{
			case PixelFormat_R8G8B8A8:
				return 32;

			case PixelFormat_R8G8B8:
				return 24;

			case PixelFormat_R4G4B4A4:
			case PixelFormat_R5G5B5A1:
			case PixelFormat_R5G6B5:
			case PixelFormat_GRAYSCALE16:
				return 16;
			}
		}


		u32 Image::RedMask() const
		{
			switch(mPixelFmt)
			{
			case PixelFormat_R8G8B8A8:
			case PixelFormat_R8G8B8:	
				return 0xFF;

			case PixelFormat_R4G4B4A4:
				return 0x0F;

			case PixelFormat_R5G5B5A1:
			case PixelFormat_R5G6B5:
				return 0x1F;

			default:
				return 0;
			}
		}


		u32 Image::GreenMask() const
		{
			switch(mPixelFmt)
			{
			case PixelFormat_R8G8B8A8:
			case PixelFormat_R8G8B8:	
				return 0xFF00;

			case PixelFormat_R4G4B4A4:
				return 0xF0;

			case PixelFormat_R5G5B5A1:
				return 0x03E0;

			case PixelFormat_R5G6B5:
				return 0x07E0;

			default:
				return 0;
			}
		}


		u32 Image::BlueMask() const
		{
			switch(mPixelFmt)
			{
			case PixelFormat_R8G8B8A8:
			case PixelFormat_R8G8B8:	
				return 0xFF0000;

			case PixelFormat_R4G4B4A4:
				return 0x0F00;

			case PixelFormat_R5G5B5A1:
				return 0x7C00;

			case PixelFormat_R5G6B5:
				return 0xF800;

			default:
				return 0;
			}
		}

		
		u32 Image::AlphaMask() const
		{
			switch(mPixelFmt)
			{
			case PixelFormat_R8G8B8A8:
				return 0xFF000000;

			case PixelFormat_R4G4B4A4:
				return 0xF000;

			case PixelFormat_R5G5B5A1:
				return 0x8000;

			default:
				return 0;
			}
		}


		u8* Image::Lock()
		{
			return mData;
		}


		void Image::Unlock()
		{
			/* Do nothing */
		}


		const u8* Image::Data() const
		{
			return mData;
		}


		Color Image::GetPixel(u32 _x, u32 _y) const
		{
			Color pix;
			if(_x >= mWidth || _y >= mHeight)
				throw "Exception";

			u32 pixIdx = (ColorBits() / 8) * (_x +  _y * mWidth);
			u32 tmpValue = drefs((u32*)(mData + pixIdx));
			
			//extract color components
			pix.Red = tmpValue & RedMask();
			pix.Green = tmpValue & GreenMask();
			pix.Blue = tmpValue & BlueMask();
			pix.Alpha = tmpValue & AlphaMask();

			return pix;
		}


		Color Image::GetPixel(const Point2u& _pos) const
		{
			return GetPixel(_pos.X, _pos.Y);
		}

		
		void Image::SetPixel(u32 _x, u32 _y, const Color& _clr)
		{
			if(_x > mWidth || _y >= mHeight)
				throw "Exception";

			u32 pixIdx = (ColorBits() / 8) * (_x +  _y * mWidth);
			u32* ptrPix = (u32*)(mData + pixIdx);
		}


		void Image::SetPixel(const Point2u& _pos, const Color& clr)
		{
			SetPixel(_pos.X, _pos.Y, clr);
		}

		void Image::Dispose()
		{
			if(mData)
				mAlloc.Deallocate(mData);
			mData = null;
		}

		
		Image& Image::operator = (const Image& _img)
		{
			if(!isnull(mData)) Dispose();
			mWidth = _img.mWidth;
			mHeight = _img.mHeight;
			mPixelFmt = _img.mPixelFmt;
			u8  bytesPerPix = ColorBits() / 8;
			u32  totalSize = mHeight * mWidth * bytesPerPix;
			mData = mAlloc.Allocate(totalSize);
			memcpy(mData, _img.mData, totalSize);
			return *this;
		}
	}
}